
#include "Node.h"
#include "DumpObjectTree.h"
#include <iostream>

Node::Node(QString nodeType, int numChildren, QString operatorSymbol) {

    //    leftChild = "left_child";
    //    rightChild = "right_child";
    //    singleChild = "single_child";

    // nodeType is check bellow
    Q_ASSERT_X(numChildren >= 0 and numChildren <= 2, "Node::Node",
            "numChildren must be 0 to 2.");

#ifdef useFrame
    setFrameStyle(QFrame::Box | QFrame::Plain);
    setLineWidth(1);
    setMidLineWidth(1);
    setContentsMargins(1, 1, 1, 1);
#endif

    m_nodeType = nodeType;
    m_numChildren = numChildren;
    m_nodeOperator = operatorSymbol;

    // set up the operator precedence table
    // Even numbers or evaluated left to right
    // Odd numbers are evaluated right to left
    // The higher the number the higher in the inverted parse tree it should
    // go or closer to the root.
    // Lower numbers will be evaluated first

    // key = nodeType; value = operator precedence
    if (nodeType == "leaf") operatorPrecedence = 10; // leaf node
    else if (nodeType == "parentheses") operatorPrecedence = 20; // ()
    else if (nodeType == "percent") operatorPrecedence = 26; // %
    else if (nodeType == "power") operatorPrecedence = 30; // ^
    else if (nodeType == "unaryPlus") operatorPrecedence = 41; // +
    else if (nodeType == "unaryMinus") operatorPrecedence = 41; // -
    else if (nodeType == "factorial") operatorPrecedence = 50; // !
    else if (nodeType == "times") operatorPrecedence = 60; // *
    else if (nodeType == "divide") operatorPrecedence = 60; // /
    else if (nodeType == "remainder") operatorPrecedence = 60; // %
    else if (nodeType == "plus") operatorPrecedence = 70; // +
    else if (nodeType == "minus") operatorPrecedence = 70; // -
    else if (nodeType == "lessThen") operatorPrecedence = 80; // <
    else if (nodeType == "greaterThen") operatorPrecedence = 80; // >
    else if (nodeType == "lessOrEqual") operatorPrecedence = 80; // <=
    else if (nodeType == "greatorOrEqual") operatorPrecedence = 80; // >=
    else if (nodeType == "equal") operatorPrecedence = 90; // =
    else if (nodeType == "notEqual") operatorPrecedence = 90; // != or ~=
    else if (nodeType == "top") operatorPrecedence = 2000;
    else {
        std::cout << "Node type is " << nodeType.toAscii().data() << std::endl;
        Q_ASSERT_X(0, "Node::Node", "Unknown node type.");
    }
}

void Node::setLeftChild(Node* child) {
    // check the inputs
    // Q_CHECK_PTR(child); // avoiding using Q_CHECK_PTR since it give
    // really cryptic errors
    Q_ASSERT_X(child, "", "Child pointer was not set.");

    // make sure this kind of node takes this kind of child
    Q_ASSERT_X(m_numChildren == 2, "Node::setLeftChild",
            "This node does not except a left child.");

    // make sure there is not an existing left child
    Q_ASSERT_X(findDirectChild<Node *>(leftChild) == 0, "",
            "This node already has a left child.");

    // set the new child name and add the new child
    child->setObjectName(leftChild);
    child->setParent(this);

    // place new child in layout, if any
    if (layout()) {
        QBoxLayout* boxLayout = qobject_cast<QBoxLayout *>(layout());
        if (boxLayout) {
            boxLayout->insertWidget(0, child);
            boxLayout->setAlignment(child, Qt::AlignHCenter);
        } else {
            Q_ASSERT_X(0, "Node::setLeftChild", "Layout type is not a QBoxLayout.");
        }
    }

    child->upDateSize();
    child->show();
}

void Node::setRightChild(Node* child) {
    // check the inputs
    Q_ASSERT_X(child, "", "Child pointer was not set.");

    // make sure this kind of node takes this kind of child
    Q_ASSERT_X(m_numChildren == 2, "Node::setRightChild",
            "This node does not except a right child.");

    // make sure there is not an existing right child
    Q_ASSERT_X(findDirectChild<Node *>(rightChild) == 0, "",
            "This node already has a right child.");

    // set the new child name and add the new child
    child->setObjectName(rightChild);
    child->setParent(this);

    // place new child in layout, if any
    if (layout()) {
        QBoxLayout* boxLayout = qobject_cast<QBoxLayout *>(layout());
        if (boxLayout) {
            boxLayout->insertWidget(3, child);
            boxLayout->setAlignment(child, Qt::AlignHCenter);
        } else {
            Q_ASSERT_X(0, "Node::setRightChild", "Layout type is not a QBoxLayout.");
        }
    }

    child->upDateSize();
    child->show();
}

void Node::setSingleChild(Node * child) {
    // check the inputs
    Q_ASSERT_X(child, "", "Child pointer was not set.");

    // make sure this kind of node takes this kind of child
    Q_ASSERT_X(m_numChildren == 1, "Node::setSingleChild",
            "This node does not except a single child.");

    // make sure there is not an existing left child
    Q_ASSERT_X(findDirectChild<Node *>(singleChild) == 0, "Node::setSingleChild",
            "This node already has a child.");

    // set the new child name and add the new child
    child->setObjectName(singleChild);
    child->setParent(this);

    // place new child in layout, if any
    if (layout()) {
        QBoxLayout* boxLayout = qobject_cast<QBoxLayout *>(layout());
        if (boxLayout) {
            boxLayout->insertWidget(m_index, child);
        } else {
            Q_ASSERT_X(0, "Node::setSingleChild", "Layout type is not a QBoxLayout.");
        }
    }

    child->upDateSize();
    child->show();
}

QString Node::getNodeType() const {
    Q_ASSERT_X(!m_nodeType.isEmpty(), "Node::getNodeType",
            "Node type was not initialized.");
    return m_nodeType;
}

QString Node::getNodeValue() const {
    Q_ASSERT_X(!m_nodeOperator.isEmpty(), "Node::getNodeValue",
            "Node value was not initialized.");
    return m_nodeOperator;
}

QString Node::toCasString() const {
    Q_ASSERT_X(getLeftChild() != 0, "Node::toCasString",
            "Could not find left hand child node.");
    Q_ASSERT_X(getRightChild() != 0, "Node::toCasString",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "Node::toCasString",
            "Expected two children.");
    QString str;
    str.append("(");
    if (getLeftChild()->getNodeType() == "empty") str.append("_");
    else str.append(getLeftChild()->toCasString());
    str.append(getNodeValue());
    if (getRightChild()->getNodeType() == "empty") str.append("_");
    else str.append(getRightChild()->toCasString());
    str.append(")");
    return str;
}

QString Node::toPrintString() const {
    Q_ASSERT_X(getLeftChild() != 0, "Node::toCasString",
            "Could not find left hand child node.");
    Q_ASSERT_X(getRightChild() != 0, "Node::toCasString",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "Node::toPrintString",
            "Expected two children.");
    QString str;
    //    str.append("(");
    if (getLeftChild()->getNodeType() == "empty") str.append("_");
    else str.append(getLeftChild()->toPrintString());
    str.append(getNodeValue());
    if (getRightChild()->getNodeType() == "empty") str.append("_");
    else str.append(getRightChild()->toPrintString());
    //    str.append(")");
    return str;
}

QString Node::toParseString() const {
    Q_ASSERT_X(getLeftChild() != 0, "Node::toCasString",
            "Could not find left hand child node.");
    Q_ASSERT_X(getRightChild() != 0, "Node::toCasString",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "Node::toPrintString",
            "Expected two children.");
    QString str;

    str.append(getLeftChild()->toParseString());
    str.append(getNodeValue());
    str.append(getRightChild()->toParseString());

    return str;
}

int Node::getInputPosition(bool& found) const {
    Q_ASSERT_X(getLeftChild() != 0, "Node::toCasString",
            "Could not find left hand child node.");
    Q_ASSERT_X(getRightChild() != 0, "Node::toCasString",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "Node::toPrintString",
            "Expected two children.");
    
    int counter = 0;
    if (not found) counter += getLeftChild()->getInputPosition(found);
    if (not found) counter += 1; // whatever this node takes
    if (not found) counter += getRightChild()->getInputPosition(found);
    return counter;
}

void Node::setInputPosition(int& position) {
    Q_ASSERT_X(getLeftChild() != 0, "Node::toCasString",
            "Could not find left hand child node.");
    Q_ASSERT_X(getRightChild() != 0, "Node::toCasString",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "Node::toPrintString",
            "Expected two children.");
    
    if (position >= 0) getLeftChild()->setInputPosition(position);
    --position; // whatever this node takes
    if (position >= 0) getRightChild()->setInputPosition(position);
}

QString Node::toOpenMath() const {
    Q_ASSERT_X(0, "Node::toOpenMath", "Not yet implemented.");
    return QString("");
}

QString Node::toMathML() const {
    Q_ASSERT_X(0, "Node::toMathML", "Not yet implemented.");
    return QString("");
}

void Node::upDateSize() {
    adjustSize();
    updateGeometry();

    if (parent()) {
        Node *nodeParent = qobject_cast<Node *>(parent());
        if (nodeParent) {
            nodeParent->upDateSize();
        } else {
            Q_ASSERT_X(0, "Node::upDateSize", "Parent was not a Node class.");
        }
    }
}

void Node::focusOut() {
    Node *nodeParent = qobject_cast<Node *>(parent());
    Q_ASSERT_X(nodeParent, "Node::focusOut", "Parent was not a Node class.");
    if (nodeParent) {
        nodeParent->focusOut();
    }
}

bool Node::isLeftChild() const {
    return objectName() == leftChild;
}

bool Node::isRightChild() const {
    return objectName() == rightChild;
}

bool Node::isSingleChild() const {
    return objectName() == singleChild;
}

Node * Node::getLeftChild() const {
    Q_ASSERT_X(numChildren() == 2, "",
            "Expected two children.");
    return findDirectChild<Node *>(leftChild);
}

Node * Node::getRightChild() const {
    Q_ASSERT_X(numChildren() == 2, "",
            "Expected two children.");
    return findDirectChild<Node *>(rightChild);
}

Node * Node::getSingleChild() const {
    Q_ASSERT_X(numChildren() == 1, "",
            "Expected one child.");
    return findDirectChild<Node *>(singleChild);
}

void Node::setLastChild(Node * child) {
    Q_ASSERT_X(child, "", "child node not set.");

    if (m_numChildren == 2) {
        if (findDirectChild<Node *>(leftChild)) {
            setRightChild(child);
        } else if (findDirectChild<Node *>(rightChild)) {
            setLeftChild(child);
        } else {
            Q_ASSERT_X(0, "", "Nether left or right are empty.");
        }
    } else if (m_numChildren == 1) {
        if (getSingleChild()) {
            Q_ASSERT_X(0, "", "Single child is already set.");
        } else {
            setSingleChild(child);
        }
    } else {
        Q_ASSERT_X(0, "", "This node does not take one or two children.");
    }
}

bool Node::isConstant() const {
    Q_ASSERT_X(findDirectChild<Node *>(leftChild) != 0, "NodePlus::isConstant",
            "Could not find left hand child node.");
    Q_ASSERT_X(findDirectChild<Node *>(rightChild) != 0, "NodePlus::isConstant",
            "Could not find right hand child node.");
    Q_ASSERT_X(numChildren() == 2, "",
            "Expected two children.");
    return ( findDirectChild<Node *>(leftChild)->isConstant()
            and findDirectChild<Node *>(rightChild)->isConstant());
}

std::vector<Node*> Node::getEquals() const {
    std::vector<Node*> v;
    return v;
}

Node* Node::getTopNode() {
    Node* parentNode = qobject_cast<Node*>(parentWidget());
    if (parentNode != 0 and parentNode != nullptr) {
        if (parentNode->getNodeType() == "top") return parentNode;
        else return parentNode->getTopNode();
    } else {
        return 0;
        Q_ASSERT_X(0, "", "Parent is not a node.");
    }
}

Controller* Node::getController() {
    Node* parentNode = qobject_cast<Node*>(parentWidget());
    if (parentNode != 0 and parentNode != nullptr) {
        return parentNode->getController();
    } else {
        Q_ASSERT_X(0, "", "Parent is not a node.");
        return 0;
    }
}

void Node::keyPressEvent(QKeyEvent *event) {
    event->ignore(); // send up to parent.
}